package com.vois.jack.btmgr.blebase;

import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.os.Build;
import android.util.Log;

import com.vois.jack.btmgr.util.Logger;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class BleHelper {
    private Logger logger = Logger.getLogger(BleHelper.class);
    private BluetoothManager bluetoothManager;
    private BluetoothAdapter bluetoothAdapter;
    private Context context; // must be application context
    static private BleHelper instance;
    private int scanProcessingAction;

    static public BleHelper getInstance() {
        if(instance == null) {
            instance = new BleHelper();
        }

        return instance;
    }

    public void init(Context context) {
        this.context = context;
        bluetoothManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
        bluetoothAdapter = bluetoothManager.getAdapter();
    }

    public boolean isBluetoothOn() {
        if(bluetoothAdapter != null) {
            return bluetoothAdapter.isEnabled();
        }

        return false;
    }

    public List<BluetoothDevice> getConnectedBleDevices() {
        if(bluetoothManager != null) {
            return bluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
        }

        return null;
    }

    public boolean isBleDeviceConnected(BluetoothDevice device) {
        List<BluetoothDevice> list = getConnectedBleDevices();

        if(list != null) {
            for(BluetoothDevice bluetoothDevice: list) {
                if(bluetoothDevice.getAddress().equals(device.getAddress())) {
                    return true;
                }
            }
        }

        return false;
    }

    public void deInit() {
        context = null;
        bluetoothManager = null;
    }

    boolean isScanProcessing() {
        return scanProcessingAction != 0;
    }

    @TargetApi(Build.VERSION_CODES.M)
    void startScanLeBluetooth(List<ScanFilter> filters, int matchRule, ScanCallback scanCallback, int scanMode) {
        logger.d("startScanLeBluetooth scanProcessingAction:" + scanProcessingAction);

        if (scanProcessingAction == 0) {
            BluetoothLeScanner leScanner = bluetoothAdapter.getBluetoothLeScanner();
            if (leScanner != null) {
                ScanSettings.Builder scanSettingBuilder = new ScanSettings.Builder().
                        setScanMode(scanMode).
                        setCallbackType(matchRule).
                        setMatchMode(ScanSettings.MATCH_MODE_STICKY);

                if (filters == null) {
                    filters = new ArrayList<ScanFilter>();
                    ScanFilter scanFilter = new ScanFilter.Builder().build();
                    filters.add(scanFilter);
                }

                if (bluetoothAdapter.isEnabled()) {
                    leScanner.startScan(filters, scanSettingBuilder.build(), scanCallback);
                }
            }
        }

        scanProcessingAction++;
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    void startScanLeBluetooth(BluetoothAdapter.LeScanCallback scanCallback) {
        logger.d("startScanLeBluetooth scanProcessingAction:" + scanProcessingAction);
        if (scanProcessingAction == 0) {
            bluetoothAdapter.startLeScan(scanCallback);
        }

        scanProcessingAction++;
    }

    @TargetApi(Build.VERSION_CODES.M)
    void stopScanLeBluetooth(ScanCallback scanCallback) {
        logger.d("stopScanLeBluetooth scanProcessingAction:" + scanProcessingAction);
        BluetoothLeScanner leScanner = bluetoothAdapter.getBluetoothLeScanner();
        if (scanProcessingAction == 0) {
            return;
        }

        scanProcessingAction--;

        if (scanProcessingAction == 0) {
            if (leScanner != null) {
                leScanner.stopScan(scanCallback);
            }
        }
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    void stopScanLeBluetooth(BluetoothAdapter.LeScanCallback scanCallback) {
        logger.d("stopScanLeBluetooth scanProcessingAction:" + scanProcessingAction);
        if (scanProcessingAction == 0) {
            return;
        }

        scanProcessingAction--;

        if (scanProcessingAction == 0) {
            bluetoothAdapter.stopLeScan(scanCallback);
        }
    }

    BluetoothGatt connectBleDevice(BluetoothDevice device, BluetoothGattCallback callback) {
        BluetoothGatt bluetoothGatt = null;
        if (device != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                bluetoothGatt = device.connectGatt(context, false, callback,
                        BluetoothDevice.TRANSPORT_LE, BluetoothDevice.PHY_LE_1M/*, mHandler*/);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                bluetoothGatt = device.connectGatt(context, false, callback,
                        BluetoothDevice.TRANSPORT_LE);
            } else {
                bluetoothGatt = device.connectGatt(context, false, callback);
            }
        }

        return bluetoothGatt;
    }

    boolean refreshDeviceCache(BluetoothGatt bluetoothGatt) {
        /*
         * There is a refresh() method in BluetoothGatt class but for now it's hidden.
         * We will call it using reflections.
         */
        try {
            final Method refresh = bluetoothGatt.getClass().getMethod("refresh");
            return (Boolean) refresh.invoke(bluetoothGatt);
        } catch (final Exception e) {

        }

        return false;
    }
}
